# -*- coding: utf-8 -*-
import datetime

from django.db import models, transaction
from django.contrib.auth.models import User
# Create your models here.


from settings import OKNO

from util import ru_date, get_yandex_inflections
from util.bitfield import BitField

class Category(models.Model):
    '''
    Категория получателя льготы
    '''
    
    title = models.CharField(u'Название категории', max_length=255)
    number = models.CharField(u'Номер категории', max_length=3, db_index=True)
    
    class Meta:
        ordering = ['number']
        verbose_name_plural = u'Категории'

    def __unicode__(self):
        return u'%s %s' % (self.number, self.title)
    
class Law(models.Model):
    
    number = models.CharField(u'Номер группы услуг', max_length=6, db_index=True)
    title = models.CharField(u'Название группы услуг', max_length=255)
    description = models.TextField(u'Описание')
    
    class Meta:
        ordering = ['number']
        verbose_name = u'Закон'
        verbose_name_plural = u'Законы'
    
    def __unicode__(self):
        return u'%s %s' % (self.number, self.title)

class Service(models.Model):
    '''
    Вид льготы
    '''
    group = models.ForeignKey(Law)
    number = models.CharField(max_length=3, db_index=True)
    title = models.CharField(max_length=512)
    
    categories = models.ManyToManyField(Category)
    
    class Meta:
        ordering = ['group', 'number']
        verbose_name = u'Услуга'
        verbose_name_plural = u'Услуги'
        
    #class Admin:
    #    list_display = ('title', 'content', 'datetime')
    
    def __unicode__(self):
        return u'%s.%s %s' % (self.group.number, self.number, self.title)

class DocumentGroup(models.Model):
    
    title = models.CharField(u'Название группы', max_length=255)
    number = models.CharField(u'Номер группы', max_length=3, db_index=True)
    
    class Meta:
        ordering = ['id']
        verbose_name_plural = u'Группы документов'
    
    def __unicode__(self):
        return u'%s %s' % (self.number, self.title)

class Document(models.Model):
    '''
    Предоставляемый получателем льготы документ
    '''
    group = models.ForeignKey(DocumentGroup, verbose_name=u'Группа документов')
    number = models.CharField(u'Номер документа в группе', max_length=3, db_index=True)
    title = models.CharField(u'Название документа', max_length=255)
    
    # Предполагаемое количество страниц в документе
    pages = models.IntegerField(default=1)
    
    services = models.ManyToManyField(Service)
    categories = models.ManyToManyField(Category)
    
    class Meta:
        ordering = ['group', 'number']
        verbose_name = u'Документ'
        verbose_name_plural = u'Документы'
    
    def __unicode__(self):
        return u'%s.%s %s' % (self.group.number, self.number, self.title)

class Area(models.Model):
    title = models.CharField(u'Район', max_length=20)
    title_in = models.CharField(u'В районе (каком?)', max_length=20)
    title_by = models.CharField(u'По району (какому?)', max_length=20)
    
    org = models.CharField(u'Организация', max_length=255)
    
   
    class Meta:
        ordering = ['title']
        verbose_name = u'Район'
        verbose_name_plural = u'Районы'
   
    def __unicode__(self):
        return self.title

class PostOffice(models.Model):
    title = models.CharField(u'Заголовок', max_length=50, unique=True)
    description = models.TextField(u'Описание', blank=True)
   
    class Meta:
        ordering = ['title']
        verbose_name = u'Почтовое отделение'
        verbose_name_plural = u'Почтовые отделения'
   
    def __unicode__(self):
        return self.title
    
'''
Модели АСП
'''

class ASPAddresses(models.Model):
    '''
        Адрес регистрации ФЛ
    '''
    class Meta:
        db_table = 'fra'
    
    pind = models.CharField(u'Почтовый индекс', max_length=90) #S4
    nasp = models.CharField(u'Населенный пункт', max_length=90) #S5
    ylic = models.CharField(u'Улица', max_length=90) #S6
    
    ndom = models.CharField(u'Номер дома', max_length=50) #FRD
    
    nkorp = models.CharField(u'Номер корпуса', max_length=50)
    nkw = models.CharField(u'Номер квартиры', max_length=50)
    nkomn = models.CharField(u'Номер комнаты', max_length=15)
    
    @property
    def post_index(self):
        if isinstance(self.pind, (unicode, str)):
            return self.pind.strip()
        return u''
    
    @property
    def number(self):
        if isinstance(self.ndom, (unicode, str)):
            return self.ndom.strip()
        return u''
    
    @property
    def locality(self):
        if isinstance(self.nasp, (unicode, str)):
            return self.nasp.strip()
        return u''
    
    @property
    def street(self):
        if isinstance(self.ylic, (unicode, str)):
            return self.ylic.strip()
        return u''
    
    #TODO: сделать проверку на существование адреса и возвращать в случае ошибки пустую строку.
    def __unicode__(self):
        address = []
        
        if self.post_index:
            address.append(self.post_index.strip())
        
        if self.locality:
            address.append(self.locality.strip())
        
        if self.street:
            address.append(self.street.strip())
        
        if self.number:
            address.append(u'д. %s'%self.number.strip())
        
        if self.nkorp:
            address.append(u'корп. %s'%self.nkorp.strip())
        
        if self.nkw:
            address.append(u'кв. %s'%self.nkw.strip())
        
        if self.nkomn:
            address.append(u'комн. %s'%self.nkomn.strip())
        
        return u', '.join(address)

class AccountingAttribute(models.Model):
    '''
        Признаки учета
    '''
    class Meta:
        db_table = 's2'
        
    kod = models.CharField(u'Код', max_length=6)
    nkod = models.CharField(u'Наименование', max_length=80)
    
    @property
    def title(self):
        return self.nkod
    
    def __unicode__(self):
        return self.title

class ASPPostOffice(models.Model):
    '''
        Почтовые отделения
    '''
    class Meta:
        db_table = 's28'
        
    kod = models.CharField(u'Код', max_length=6)
    nkod = models.CharField(u'Наименование', max_length=80)
    
    @property
    def title(self):
        return self.nkod
    
    def __unicode__(self):
        return self.title

class Bank(models.Model):
    '''
        Банки и счета
    '''
    class Meta:
        db_table = 's29'
        
    kod = models.CharField(u'Код', max_length=6)
    nkod = models.CharField(u'Наименование', max_length=80)
    
    @property
    def title(self):
        return self.nkod
    
    def __unicode__(self):
        return self.title
  
class IssuingAgency(models.Model):
    '''
        Учреждения, выдающие документы
    '''
    class Meta:
        db_table = 's31'
        
    kod = models.CharField(u'Код', max_length=6)
    nkod = models.CharField(u'Наименование', max_length=80)
    
    @property
    def title(self):
        if isinstance(self.nkod, (unicode, str)):
            return self.nkod
        return u''
    
    def __unicode__(self):
        return self.title

class DeregistrationReason(models.Model):
    '''
        Таблица причин снятия с учета
    '''
    class Meta:
        db_table = 's22'
        
    kod = models.CharField(u'Код', max_length=6)
    nkod = models.CharField(u'Наименование', max_length=80)
    
    @property
    def title(self):
        return self.nkod
    
    def __unicode__(self):
        return self.title 

class CitizenInflectCache(models.Model):
    '''
        Кеш склонения ФИО граждан
    '''
    
    nominative = models.CharField(u'Именительный падеж', max_length=50, unique=True)
    genitive = models.CharField(u'Родительный падеж', max_length=50)
    dative = models.CharField(u'Дательный падеж', max_length=50)
    accusative = models.CharField(u'Винительный падеж', max_length=50)
    ablative = models.CharField(u'Творительный падеж', max_length=50)
    prepositional = models.CharField(u'Предложный падеж', max_length=50)
    
    class Meta:
        verbose_name = u'ФИО'
        verbose_name_plural = u'ФИО'
    
    def __unicode__(self):
        return self.nominative
   
class Citizen(models.Model):
    '''
        Таблица физических лиц АСП
    '''
    GENDER_CHOICES = (
        ('М', 'Мужской'),
        ('Ж', 'Женский'),
    )
    
    WORK_CHOICES = (
        ('0', 'Нет данных'),
        ('1', 'Не работает'),
        ('2', 'Работает')
    )
    
    class Meta:
        db_table = 'f2'
    
    famil = models.CharField(u'Фамилия', max_length=50)
    imja = models.CharField(u'Имя', max_length=50)
    otch = models.CharField(u'Отчество', max_length=50)
    
    drog = models.DateTimeField(u'Дата рождения')
    
    pol = models.CharField(u'Пол', max_length=1, choices=GENDER_CHOICES)
    
    #Паспорт
    psr = models.CharField(u'Серия', max_length=30)
    pnm = models.CharField(u'Номер', max_length=30)
    pdv = models.DateTimeField(u'Дата выдачи')
    pkv = models.ForeignKey(IssuingAgency, db_column='pkv', limit_choices_to={'kod__startswith': '01'},
                            verbose_name=u'Выдан')
    
    ntel = models.CharField(u'Контактный телефон', max_length=50)
    cellular = models.CharField(u'Мобильный телефон', max_length=10)
    
    fra_reg = models.ForeignKey(ASPAddresses)
    
    pku = models.CharField(u'ПКУ', max_length=11)
    
    psy_status = models.SmallIntegerField(db_column='psy')
    psy = models.ForeignKey(DeregistrationReason, db_column='psy',
                            verbose_name=u'Признак снятия с учета', max_length=25)
    
    categories = models.ManyToManyField(AccountingAttribute, through='CitizenAccountingAttributes', verbose_name=u'Признаки учета')
    post_offices = models.ManyToManyField(ASPPostOffice, through='CitizenBanksAndPostOffices', verbose_name=u'Почтовые отделения')
    #banks = models.ManyToManyField(Bank, through='CitizenBanksAndPostOffices', verbose_name=u'Банки    ')
    
    #Социальный статус
    prab = models.CharField(u'Работает?', max_length=1, choices=WORK_CHOICES)
    
    def __unicode__(self):
        return u'%s %s %s' % (self.last_name, self.first_name, self.patronymic)
    
    @property
    def last_name(self):
        if isinstance(self.famil, (unicode, str)):
            return self.famil
        return u''
    
    @property
    def first_name(self):
        if isinstance(self.imja, (unicode, str)):
            return self.imja
        return u''
    
    @property
    def patronymic(self):
        if isinstance(self.otch, (unicode, str)):
            return self.otch
        return u''

    @property
    def gender(self):
        return self.pol == u'М' and u'M' or u'F'
    
    @property
    def passport(self):
        if self.psr and self.pnm:
            psr, pnm = self.psr.strip(), self.pnm.strip()
            if psr and pnm:
                return u'%s №%s'%(psr, pnm)
        return u''
    
    @property
    def passport_vydan(self):
        if self.pdv and self.pkv:
            pdv, pkv = self.pdv.date(), self.pkv
            if pdv and pkv:
                return u'%s г., %s'%(ru_date(pdv), pkv)
        return u''
    
    @property
    def address(self):
        return self.fra_reg
    
    @property
    def phone(self):
        from util import clean_phone
        #TODO: проверить, нужно ли теперь приводить к unicode
        ntel = clean_phone(unicode(self.ntel))
        cell = clean_phone(u'8' + unicode(self.cellular))
        _phone = []
        if self.ntel:
            _phone.append(ntel)
        if self.cellular:
            _phone.append(cell)
        
        return u', '.join(_phone)
    
    @property
    def spku(self):
        pku = list(self.pku)
        pku.insert(4, ':')
        pku.insert(2, ':')    
        return u''.join(pku)
    
    @property
    def registered(self):
        return self.psy_status is None
    
    @property
    def dereg_reason(self):
        return self.psy    
  
class CitizenAccountingAttributes(models.Model):
    '''
        Связи признаков учета с гражданами
    '''
    class Meta:
        db_table = 'f1'
        
    f2 = models.ForeignKey(Citizen, db_column='f2_id', verbose_name=u'Гражданин')
    s2 = models.ForeignKey(AccountingAttribute, db_column='isu', verbose_name=u'Признак учета')
    
class CitizenBanksAndPostOffices(models.Model):
    '''
        Связи граждан с банками и почтовыми отделениями
    '''
    class Meta:
        db_table = 'f22'
        
    f2 = models.ForeignKey(Citizen, db_column='f2_id', verbose_name=u'Гражданин')
    s28 = models.ForeignKey(ASPPostOffice, db_column='svz', verbose_name=u'Почтовое отделение')
    s29 = models.ForeignKey(Bank, db_column='osb', verbose_name=u'Отделение СберБанка')
    
class Statement(models.Model):
    INFORM_BY_CHOICES = (
        (1, u'На руки'),
        (2, u'По почте'),
        (3, u'По E-mail'),
    )
    
    #Для временного прекращения использования
    #Интернет-служб склонения
    inflect_error_counter = 0;
    inflect_error_datetime = None;
    
    '''
    Заполненное заявление
    '''
    number = models.IntegerField(db_index=True, verbose_name=u'Порядковый номер')
    area = models.ForeignKey(Area, verbose_name=u'Район')
    
    user = models.ForeignKey(User, editable=False)
    
    asp_citizen = models.ForeignKey(Citizen, editable=False, db_column='citizen_id')
    citizen_inflections = models.ForeignKey(CitizenInflectCache, null=True, editable=False)
    
    phone = models.CharField(u'Номер телефона', max_length=255, blank=True)
    
    post_office = models.ForeignKey(PostOffice, verbose_name=u'Почтовое отделение',
                                    related_name='+', default=1)
    
    credit_org = models.CharField(u'Кредитная организация', max_length=255, blank=True)
    
    inform_by = BitField(
        flags=(
            ('hands', u'На руки',),
            ('post', u'По почте',),
            ('email', u'По email',),
        ),
        default=0,
        verbose_name=u'Информировать',
    )
    
    date = models.DateField(u'Дата заявления', default=datetime.date.today)
    
    categories = categories = models.ManyToManyField(Category)
    services = models.ManyToManyField(Service)
    documents = models.ManyToManyField(Document)
    
    #mv_actual_state = models.ForeignKey(Movement, editable=False)
    
    @property
    def citizen(self):
        try:
            return self.asp_citizen
        #TODO: сделать нормальную обработку ошибок и вменяемый вывод сообщения об ошибке
        except:
            raise Citizen.DoesNotExist('Statement: %i, Citizen: %i'%(self.id, self.asp_citizen_id))
    
    class Meta:
        ordering = ['-id']
    
    def __unicode__(self):
        return u'Заявление №%s от %s' % (self.number, self.date)
    
    @transaction.commit_on_success
    def regen_inflections(self):
        inflections = self.citizen_inflections
        self.citizen_inflections = None
        self.save(force_update=True)
        
        if inflections.statement_set.count() == 0:
            inflections.delete()
        #если больше 1 заявления ссылается на склонения, не удаляем
        
        if self.inflect:
            return True
        
        return False
    
    def __get_split_inflections(self):
        last_name = get_yandex_inflections(self.citizen.last_name)
        first_name = get_yandex_inflections(self.citizen.first_name)
        patronymic = get_yandex_inflections(self.citizen.patronymic)
        
        if not last_name or not first_name or not patronymic:
            return False
        
        inflect_list = {}
        for i in range(1,7):
            tmp = []
            key = unicode(i)
            if last_name.has_key(key):
                tmp.append(last_name[key])
            else:
                tmp.append(last_name['1'])
                
            if first_name.has_key(key):
                tmp.append(first_name[key])
            else:
                tmp.append(first_name['1'])
                
            if patronymic.has_key(key):
                tmp.append(patronymic[key])
            else:
                tmp.append(patronymic['1'])
            
            inflect_list[key] = u' '.join(tmp)
            
        return inflect_list
    
    def __gen_inflect_cache(self):
        fio = u' '.join((self.citizen.last_name, self.citizen.first_name, self.citizen.patronymic))
        
        if self.citizen_inflections:
            return True

        try:
            cache = CitizenInflectCache.objects.get(nominative=fio)
        except CitizenInflectCache.DoesNotExist:
            inflect_list = get_yandex_inflections(fio)
            if inflect_list:
                if not inflect_list.has_key('2'):
                    inflect_list = self.__get_split_inflections()
                    if not inflect_list:
                        return False
                
                cache = CitizenInflectCache(
                    nominative = inflect_list['1'],
                    genitive = inflect_list['2'],
                    dative = inflect_list['3'],
                    accusative = inflect_list['4'],
                    ablative = inflect_list['5'],
                    prepositional = inflect_list['6'],
                )
                cache.save(force_insert=True)
                
            else:
                return False
            
        self.citizen_inflections = cache
        self.save(force_update=True)
        
        return True
    
    @property
    def inflect(self):        
        if not OKNO['yandex_inflect']:
            return False
        
        if Statement.inflect_error_counter >= OKNO['inflect_max_errors']:
            if not Statement.inflect_error_datetime:
                Statement.inflect_error_datetime = datetime.datetime.now()
                return False
            else:
                delta = (datetime.datetime.now() - Statement.inflect_error_datetime).seconds/60
                if delta >= OKNO['inflect_error_timeout']:
                    Statement.inflect_error_counter = 0
                    Statement.inflect_error_datetime = None
                else:
                    return False
                    
        
        if not self.citizen_inflections and not self.__gen_inflect_cache():
            Statement.inflect_error_counter = Statement.inflect_error_counter + 1
            return False
            
        return self.citizen_inflections
